#include "socket.hpp"

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include <limits>
#include <iostream>

TCPSocket::TCPSocket(const std::string &addr, const int port) : addr_str(addr), port(port)
{
    memset(&addr_info, 0, sizeof(addr_info));
    addr_info.sin_family = AF_INET;
    auto inaddr = inet_addr(addr_str.c_str());
    if (inaddr != INADDR_NONE)
    {
        memcpy(&(addr_info.sin_addr), &inaddr, sizeof(inaddr));
    }
    else
    {
        struct hostent *hostent_ptr = gethostbyname(addr_str.c_str());
        if (hostent_ptr != NULL)
        {
            memcpy(&(addr_info.sin_addr), hostent_ptr->h_addr, hostent_ptr->h_length);
        }
        else
        {
            std::cerr << __LINE__ << ' ' << __FILE__ << ' ' << "gethostbyname()" << std::endl;
        }
    }
    addr_info.sin_port = htons(port);
    sockfd = socket(AF_INET, SOCK_STREAM, 0); //TCP socket
    if (sockfd < 0)
    {
        std::cerr << __LINE__ << ' ' << __FILE__ << ' ' << "socket(), sockefd < 0 " << std::endl;
    }
}

int TCPSocket::connect() const
{
    int status = ::connect(sockfd, (struct sockaddr *)&addr_info, sizeof(addr_info));
    return status;
}

int TCPSocket::get_fd() const
{
    return sockfd;
}

TCPSocket::~TCPSocket()
{
    close(sockfd);
}

int TCPSocket::write(const std::string &msg)
{
    size_t nbytes = msg.size();
    size_t nwritten = 0;
    while ((nwritten = ::write(sockfd, msg.c_str() + nwritten, nbytes - nwritten)) != nbytes)
    {
    }
    return nwritten;
}

std::string TCPSocket::read(int nbytes)
{
    if (nbytes == -1)
    {
        nbytes = std::numeric_limits<int>().max();
    }
    std::string res;
    char buf[4096];
    size_t nread = 0;
    while ((nread = ::read(sockfd, buf, sizeof(buf))) > 0 && res.size() < nbytes)
    {
        buf[nread] = '\0';
        res += buf;
    }
    if (nread < 0)
    {
        res += "<! error !>";
    }
    return res;
}